/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.jmark;

import com.jspx.boot.environment.Environment;
import com.jspx.io.AbstractRead;
import com.jspx.io.AbstractWrite;
import com.jspx.io.AutoReadTextFile;
import com.jspx.io.WriteFile;
import com.jspx.scriptmark.ScriptmarkEnv;
import com.jspx.scriptmark.core.ScriptMarkEngine;
import com.jspx.json.JSONArray;
import com.jspx.json.JSONObject;
import com.jspx.scriptmark.load.InputStreamSource;
import com.jspx.scriptmark.load.Source;
import com.jspx.scriptmark.load.StringSource;
import com.jspx.sober.annotation.Table;
import com.jspx.sober.config.SoberColumn;
import com.jspx.sober.util.AnnotationUtil;
import com.jspx.txweb.table.MemberCourt;
import com.jspx.txweb.table.MemberDept;
import com.jspx.utils.*;
import org.htmlcleaner.*;
import java.io.File;
import java.io.StringWriter;
import java.io.Writer;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: chenYuan
 * Date: 2010-7-8
 * Time: 15:12:38
 * 生成UI
 * com.jspx.jmark.JspxCodeMaker
 */
public class JspxCodeMaker {
    public static final String varClassName = "className";
    public static final String varPackageName = "packageName";
    public static final String varDate = "date";
    public static final String varEncode = "encode";
    public static final String varNamespace = "namespace";
    public static final String varFilePath = "filePath";
    public static final String varName = "varName";

    private static final String ui_templet = "aeui.html";
    private static final String acui_templet = "acui.html";
    private static final String validation_templet = "validation.html";
    private static final String[] jumpFields = new String[]{"id", "putUid", "putName", "ip", "createDate"};

    public final static String[] fileNames = {"View.ftl","ListView.ftl","DAO.ftl","DAOImpl.ftl","Action.ftl"};
    public final static String[] paths = {"view","view","dao","dao/impl","action"};
    public final static String[] outNames = {"${varName}View","${varName}ListView","${varName}DAO","${varName}DAOImpl","${varName}Action"};
    public static String[] configFiles = {"sioc","txweb","validator","sqlmap"};

    private Class builderClass;

    public  JspxCodeMaker(String className)
    {
        try {
            builderClass = ClassUtil.loadClass(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private String getClassVarName()
    {
        return com.jspx.utils.StringUtil.substringAfterLast(builderClass.getName(), ".");
    }

    private String getVarName()
    {
        return com.jspx.utils.StringUtil.uncapitalize(com.jspx.utils.StringUtil.substringAfterLast(builderClass.getName(), "."));
    }

    private  String callScriptMarkEngine(Map<String, Object> map, String templet) throws Exception {
        Source reader = new InputStreamSource(JspxCodeMaker.class.getResourceAsStream(templet), templet, Environment.defaultEncode);
        ScriptMarkEngine scriptMarkEngine = new ScriptMarkEngine(ScriptmarkEnv.noCache, reader, null);
        Writer writer = new StringWriter();
        scriptMarkEngine.process(writer, map);
        writer.close();
        return writer.toString();
    }

    public  String callScriptMarkEngine(Source source, Map<String, Object> map) throws Exception {
        ScriptMarkEngine scriptMarkEngine = new ScriptMarkEngine(ScriptmarkEnv.noCache, source, null);
        Writer writer = new StringWriter();
        scriptMarkEngine.process(writer, map);
        writer.close();
        return writer.toString();
    }

    /**
     * 生成添加编辑窗体
     *
     * @throws Exception
     */
    public  String makerUI() throws Exception {

        List<SoberColumn> soberColumns = AnnotationUtil.getColumnList(builderClass);
        boolean haveDate = false;
        for (SoberColumn soberColumn:soberColumns)
        {
           if (soberColumn.getClassType()==Date.class)
           {
               haveDate = true;
               break;
           }
        }
        Map<String, Object> valueMap = new HashMap<String, Object>();
        String title = AnnotationUtil.getTableCaption(builderClass);
        valueMap.put("title", title);
        valueMap.put("haveDate", haveDate);
        valueMap.put("columns", soberColumns);
        valueMap.put("jumpFields", jumpFields);
        valueMap.put(varName,getVarName());
        valueMap.put(varClassName,getClassVarName());

        return callScriptMarkEngine(valueMap, ui_templet);
    }

    public  String makerCUI() throws Exception {

        List<SoberColumn> soberColumns = AnnotationUtil.getColumnList(builderClass);
        boolean haveDate = false;
        for (SoberColumn soberColumn:soberColumns)
        {
            if (soberColumn.getClassType()==Date.class)
            {
                haveDate = true;
                break;
            }
        }
        Map<String, Object> valueMap = new HashMap<String, Object>();
        String title = AnnotationUtil.getTableCaption(Table.class);
        valueMap.put("title", title);
        valueMap.put("haveDate", haveDate);
        valueMap.put("columns", soberColumns);
        valueMap.put("jumpFields", jumpFields);
        valueMap.put(varName,getVarName());
        valueMap.put(varClassName,getClassVarName());
        return callScriptMarkEngine(valueMap, acui_templet);
    }


    /**
     * 生成验证配置
     *
     * @throws Exception
     */
    public  String makerValidator() throws Exception {

        List<SoberColumn> soberColumns = AnnotationUtil.getColumnList(builderClass);
        Map<String, Object> valueMap = new HashMap<String, Object>();
        valueMap.put("columns", soberColumns);
        valueMap.put("jumpFields", jumpFields);
        valueMap.put(varName,getVarName());
        valueMap.put(varClassName,getClassVarName());

        return callScriptMarkEngine(valueMap, validation_templet);
    }
    /**
     * 生成js 的表格 json信息
     * @throws Exception
     */
    public  String makerTableColumn() {

        List<SoberColumn> soberColumns = AnnotationUtil.getColumnList(builderClass);
        JSONArray jsonArray = new JSONArray();
        for (SoberColumn column : soberColumns) {
            if (column.isHidden()) continue;
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("name", column.getName());
            jsonObject.put("caption", column.getCaption());
            if (ArrayUtil.contains(new String[]{"int", "long", "float", "double"}, column.getType())) {
                jsonObject.put("type", "number");
            } else
                jsonObject.put("type", column.getType());

            if (!StringUtil.isNULL(column.getOption())) {
                jsonObject.put("option", column.getOption());
                jsonObject.put("input", "select");
            } else
                jsonObject.put("input", column.getInput());
            if ("id".equalsIgnoreCase(column.getName()))
                jsonObject.put("edit", true);
            else jsonObject.put("edit", false);
            jsonArray.put(jsonObject);
        }
        return StringUtil.replace(jsonArray.toString(), "},{", "},\r\n{");
    }


    public  void javaMaker(Map<String, Object> map) throws Exception {
        Map<String, Object> valueMap = new HashMap<String, Object>(map);
        String title = AnnotationUtil.getTableCaption(builderClass);
        valueMap.put("title", title);
        valueMap.put(varName,getVarName());
        valueMap.put(varClassName,getClassVarName());
        valueMap.put(varDate,new Date());

        for (int i = 0; i < fileNames.length; i++) {
            String fileName = fileNames[i];
            Source source = new InputStreamSource(JspxCodeMaker.class.getResourceAsStream(fileName), fileName, Environment.defaultEncode);
            String outStr = callScriptMarkEngine(source, valueMap);
            String fileOutPath = FileUtil.mendPath(FileUtil.mendPath((String)valueMap.get(varFilePath)) + StringUtil.replace(callScriptMarkEngine(new StringSource("${" + varPackageName + "}"), valueMap), ".", "/") + "/" + paths[i]);
            FileUtil.makeDirectory(fileOutPath);
            String wFile = fileOutPath + StringUtil.capitalize(callScriptMarkEngine(new StringSource(outNames[i]), valueMap)) + ".java";
            if (!FileUtil.isFileExist(wFile))
            {
                AbstractWrite aw = new WriteFile();
                aw.setEncode(Environment.defaultEncode);
                aw.setFile(wFile);
                aw.setContent(outStr);
            }
        }
    }


    public  void makeConfig(Map<String, Object> valueMap) throws Exception {
        for (String fileName : configFiles) {
            Source source = new InputStreamSource(JspxCodeMaker.class.getResourceAsStream(fileName + ".ftl"), fileName, Environment.defaultEncode);
            String outStr = callScriptMarkEngine(source, valueMap);
            String wFile = FileUtil.mendPath(FileUtil.mendPath((String)valueMap.get(varFilePath)) + callScriptMarkEngine(new StringSource("${namespace}"), valueMap) + "." + fileName + ".xml");
            if (!FileUtil.isFileExist(wFile)) {
                AbstractWrite aw = new WriteFile();
                aw.setEncode(Environment.defaultEncode);
                aw.setFile(wFile);
                aw.setContent(outStr);
            }
        }
    }

    public  String getConfigIocXML(Map<String, Object> valueMap) {
        String className =  StringUtil.uncapitalize(StringUtil.substringAfterLast(builderClass.getName(), "."));
        StringBuilder configTxt = new StringBuilder();

        configTxt.append("<!--ioc " + AnnotationUtil.getTableCaption(builderClass) + "-->\r\n");
        //DAO
        configTxt.append("<bean id=\"").append(className).append("DAO\" class=\"").append(valueMap.get(varPackageName)).append(".dao.impl.").append(StringUtil.capitalize(className)).append("DAOImpl\" singleton=\"true\" >").append("\r\n");
        configTxt.append("<ref name=\"soberFactory\">jspxSoberFactory</ref>\r\n</bean>\r\n");
        //ioc
        configTxt.append("<bean id=\"").append(className).append("ListView\" class=\"").append(valueMap.get(varPackageName)).append(".view.").append(StringUtil.capitalize(className)).append("ListView\" singleton=\"false\" />").append("\r\n");
        configTxt.append("<bean id=\"").append(className).append("Action\" class=\"").append(valueMap.get(varPackageName)).append(".action.").append(StringUtil.capitalize(className)).append("Action\" singleton=\"false\" />").append("\r\n");
        return configTxt.toString();
    }


    public  String getConfigTXWebXML(Map<String, Object> valueMap) {
        String className =  StringUtil.uncapitalize(StringUtil.substringAfterLast(builderClass.getName(), "."));
        StringBuilder configTxt = new StringBuilder();
        configTxt.append("<!--txweb " + AnnotationUtil.getTableCaption(builderClass) + "-->\r\n");
        configTxt.append("<action name=\"\\w+").append(className).append("\" class=\"").append(className).append("ListView\" />\r\n");
        configTxt.append("<action name=\"\\w+").append(className).append("\" class=\"").append(className).append("Action\" method=\"@method\" />\r\n");
        return configTxt.toString();
    }


    public  String makerTableColumnExcl() {

        List<SoberColumn> soberColumns = AnnotationUtil.getColumnList(builderClass);
        StringBuilder builder = new StringBuilder();
        for (SoberColumn column : soberColumns) {
            if (column.isHidden()) continue;
            builder.append(column.getName()).append("\t").append(column.getCaption()).append("\t").append(column.getOption()).append("\t").append(column.getType()).append("\r\n");
        }
        return builder.toString();
    }


    /**
     *
     * @return  提出字段给不会看程序的人浏览的
     * @throws Exception
     */
    public  String makerTableField()  {

        List<SoberColumn> soberColumns = AnnotationUtil.getColumnList(builderClass);
        StringBuilder stringBuffer = new StringBuilder();
        for (SoberColumn column : soberColumns) {
            //if (column.isHidden()) continue;
            stringBuffer.append(column.getName()).append("\t").append(column.getType());
            if (column.getLength()>0)
            stringBuffer.append("(").append(column.getLength()).append(")");
            stringBuffer.append("\t").append(column.getCaption()).append("\r\n");

        }
        return stringBuffer.toString();
    }


    /**
     * IOC
     * @return
     */
    public  String iocDefine()  {

        List<SoberColumn> soberColumns = AnnotationUtil.getColumnList(builderClass);
        StringBuilder stringBuffer = new StringBuilder();
        for (SoberColumn column : soberColumns) {
            //if (column.isHidden()) continue;
            stringBuffer.append(column.getName()).append("\t").append(column.getType());
            if (column.getLength()>0)
                stringBuffer.append("(").append(column.getLength()).append(")");
            stringBuffer.append("\t").append(column.getCaption()).append("\r\n");

        }
        return stringBuffer.toString();
    }

    //将AE页面转换为AC页面，编辑页面转换为浏览页面
    //transAEToAc("D:\\website\\webapps\\root\\juweb\\admin\\acspaceconfig.ftl");

    //将AE页面转换为AC页面，编辑页面转换为浏览页面
    public  String transAeToAc(String aeFile) throws Exception {

        AbstractRead read = new AutoReadTextFile();
        read.setEncode(Environment.encode);
        read.setFile(aeFile);
        String html = read.getContent();
        HtmlCleaner cleaner = new HtmlCleaner();
        CleanerProperties props = cleaner.getProperties();
        props.setAdvancedXmlEscape(false);

        props.setRecognizeUnicodeChars(false);
        props.setTranslateSpecialEntities(false);
        props.setUseCdataForScriptAndStyle(false); //是否使用 <![CDATA[

        props.setOmitDeprecatedTags(true);
        props.setOmitXmlDeclaration(true);
        props.setOmitHtmlEnvelope(true);
        props.setOmitUnknownTags(false);

        props.setIgnoreQuestAndExclam(true);
        props.setNamespacesAware(false);
        props.setTransResCharsToNCR(false);
        props.setUseEmptyElementTags(false);

        TagNode node = cleaner.clean(html);
        List set = node.getAllElementsList(true);
        for (Object tag : set)
        {
           // <span id="titleMsg" class="note"></span>
            TagNode tagNode = (TagNode) tag;
            if ("note".equalsIgnoreCase(tagNode.getAttributeByName("class")))
            {
                tagNode.removeFromTree();
            }
            if ("input".equalsIgnoreCase(tagNode.getName()))
            {
                String value = tagNode.getAttributeByName("value");
                HtmlNode newNode = new ContentNode(value);
                tagNode.getParent().addChild(newNode);
                tagNode.removeFromTree();
            }
            if ("label".equalsIgnoreCase(tagNode.getName()))
            {
                tagNode.removeAttribute("for");
            }

            if ("textarea".equalsIgnoreCase(tagNode.getName()))
            {
                HtmlNode newNode = new ContentNode(tagNode.getText().toString());
                tagNode.getParent().addChild(newNode);
                tagNode.removeFromTree();
            }
        }
        return (new SimpleHtmlSerializer(props).getAsString(node));
    }

   public  void  builderClass(Map<String, Object> valueMap) throws Exception {

       String outFileName = StringUtil.substringAfterLast(builderClass.getName(), ".").toLowerCase();
       String path = FileUtil.mendPath ((String)valueMap.get(JspxCodeMaker.varFilePath));

       File iocDefineFile = new File(path,"iocDefine.java");
       File configIocFile = new File(path,"config.ioc.xml");
       File configTXWebFile = new File(path,"config.txweb.xml");

       makeConfig(valueMap);
       String tableColumn = makerTableColumn();

       File file = new File(path,outFileName +".json");
       AbstractWrite write = new WriteFile();
       write.setEncode(Environment.defaultEncode);
       write.setFile(file.getAbsolutePath());
       write.setContent(tableColumn);

       String configIoc = getConfigIocXML(valueMap);
       write = new WriteFile();
       write.setEncode(Environment.defaultEncode);
       write.setFile(configIocFile.getAbsolutePath());
       write.setContent(configIoc,true);

       String configTXweb = getConfigTXWebXML(valueMap);
       write = new WriteFile();
       write.setEncode(Environment.defaultEncode);
       write.setFile(configTXWebFile.getAbsolutePath());
       write.setContent(configTXweb,true);


       write = new WriteFile();
       write.setEncode(Environment.defaultEncode);
       write.setFile(iocDefineFile.getAbsolutePath());
       String iocOut = "static public final String " + outFileName +"DAO = \""+outFileName+"DAO\";\r\n";
       write.setContent(iocOut,true);


       String uiHtml = makerUI();  //生成 HTML UI 表单\
       file = new File(path,"ae"+ outFileName + ".ftl");
       write = new WriteFile();
       write.setEncode(Environment.defaultEncode);
       write.setFile(file.getAbsolutePath());
       write.setContent(uiHtml);

       String cuiHtml = makerCUI();  //生成 HTML CUI 表单
       file = new File(path,"ac"+ outFileName + ".ftl");
       write = new WriteFile();
       write.setEncode(Environment.defaultEncode);
       write.setFile(file.getAbsolutePath());
       write.setContent(cuiHtml);

       String validatorHtml = makerValidator();  //生成 验证配置
       file = new File(path,outFileName + ".validator.xml");
       write = new WriteFile();
       write.setEncode(Environment.defaultEncode);
       write.setFile(file.getAbsolutePath());
       write.setContent(validatorHtml);

       String exclHtml = makerTableColumnExcl();  //生成 excl 表结构
       file = new File(path,outFileName + ".xls");
       write = new WriteFile();
       write.setEncode("GBK");
       write.setFile(file.getAbsolutePath());
       write.setContent(exclHtml);

       javaMaker(valueMap);
   }

    public static void main(String args[]) throws Exception {
        String[] buildClassName = new String[]{MemberDept.class.getName(),MemberCourt.class.getName()};
        String outPath = "D:\\temp\\code";
        String packageNamePath = "jspx.user";
        String namespacePath = "user";

        Map<String, Object> valueMap = new HashMap<String, Object>();
        valueMap.put(JspxCodeMaker.varPackageName,packageNamePath); //包路径
        valueMap.put(JspxCodeMaker.varDate,new Date());
        valueMap.put(JspxCodeMaker.varNamespace,namespacePath);  //命名空间
        valueMap.put(JspxCodeMaker.varEncode,Environment.defaultEncode);
        valueMap.put(JspxCodeMaker.varFilePath, outPath);  // DAO Action View 的保存路径
        for (String className:buildClassName)
        {
            JspxCodeMaker maker = new JspxCodeMaker(className);
            maker.builderClass(valueMap);
        }
    }

}